'=====================================================================
'
'  File:    Buffer.vb
'  Summary: This file contains the implementation of several classes that support the
'           RemoveDuplicates transformation. These class are used to 
'           cache the rows of the incoming input buffers from the upstream 
'           component.
'  Date:    06/15/2004
'
'---------------------------------------------------------------------
'
'   This file is part of the Microsoft SQL Server Code Samples.
'   Copyright (C) Microsoft Corporation.  All rights reserved.
'
'   This source code is intended only as a supplement to Microsoft
'   Development Tools and/or on-line documentation.  See these other
'   materials for detailed information regarding Microsoft code samples.
'
'   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
'   ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
'   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
'   PARTICULAR PURPOSE.
'
'===================================================================== 
Imports System
Imports System.Diagnostics
Imports System.Collections
Imports Microsoft.SqlServer.Dts.Pipeline
Imports Microsoft.SqlServer.Dts.Pipeline.Wrapper
Imports Microsoft.SqlServer.Dts.Runtime.Wrapper

Public Class Buffer
    Private rows As ArrayList
    Private m_columnInfos As ColumnInfos

    Public Sub New()
        rows = New ArrayList()
        m_columnInfos = New ColumnInfos()
    End Sub

    Public Sub New(ByVal columnInfos As ColumnInfos)
        rows = New ArrayList()
        m_columnInfos = columnInfos
    End Sub

    '/ <summary>
    '/ Sorts the rows in the buffer.
    '/ </summary>
    Public Sub Sort()
        rows.Sort()
    End Sub

#Region "Properties"

    Public ReadOnly Property RowCount() As Integer
        Get
            Return rows.Count
        End Get
    End Property

    Public ReadOnly Property ColumnCount() As Integer
        Get
            Return Me.ColumnInfos.Count
        End Get
    End Property

    Default Public ReadOnly Property Item(ByVal index As Integer) As Row
        Get
            Return CType(rows(index), Row)
        End Get
    End Property

    Public Property ColumnInfos() As ColumnInfos
        Get
            Return m_columnInfos
        End Get
        Set(ByVal value As ColumnInfos)
            m_columnInfos = value
        End Set
    End Property
#End Region

#Region "Add Row"

    '/ <summary>
    '/ Copy the columns from the input buffer into 
    '/ the internal buffer. The InputBufferColumnIndex property contains the index of the 
    '/ column in the input buffer.
    '/ </summary>
    '/ <param name="buffer">The PipelineBuffer provided to the component in ProcessInput.</param>
    Public Overloads Sub AddRow(ByVal buffer As PipelineBuffer)
        If buffer Is Nothing Then
            Throw New ArgumentNullException("buffer")
        End If

        Dim cols(Me.ColumnInfos.Count) As Object

        For col As Integer = 0 To ColumnInfos.Count - 1
            Dim ci As ColumnInfo = ColumnInfos(col)
            cols(col) = buffer(ci.inputBufferColumnIndex)
        Next

        rows.Add(New Row(cols))
    End Sub

    Public Overloads Sub AddRow(ByVal row As Row)
        rows.Add(row)
    End Sub
#End Region
End Class

<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1036:OverrideMethodsOnComparableTypes")> _
Public Class Row
    Implements IComparable

    Private columnData() As Object

    Public Sub New(ByVal columnValues() As Object)
        columnData = New Object(columnValues.Length) {}

        ' assume the data provided is 
        ' in the same order as the column definitions.
        Dim x As Integer

        For x = 0 To columnValues.Length - 1
            columnData(x) = columnValues(x)
        Next x

    End Sub

    Default Public Property Item(ByVal index As Integer) As Object
        Get
            Return columnData(index)
        End Get

        Set(ByVal Value As Object)
            columnData(index) = Value
        End Set
    End Property

    Public Shadows Function Equals(ByVal row As Row) As Boolean
        For x As Integer = 0 To (Me.columnData.Length) - 1
            If columnData(x) Is Nothing AndAlso Not (row.columnData(x) Is Nothing) Then
                Return False
            ElseIf Not (columnData(x) Is Nothing) AndAlso row.columnData(x) Is Nothing Then
                Return False
            ElseIf Not (columnData(x) Is Nothing) And Not (row.columnData(x) Is Nothing) Then
                If CompareColumns(columnData(x), row.columnData(x)) <> 0 Then
                    Return False
                End If
            End If
        Next

        Return True
    End Function
#Region "IComparable Members"

    '
    ' Compare two rows.
    ' 
    ' Parameters: object; A row to compare to this row.</param>
    '
    ' Return Value:
    ' Less than zero;  This instance is less than obj.  
    ' Zero; This instance is equal to obj.  
    ' Greater than zero;  This instance is greater than obj.  
    '
    Public Function CompareTo(ByVal obj As Object) As Integer Implements IComparable.CompareTo
        Dim compareRow As Row = CType(obj, Row)

        For col As Integer = 0 To columnData.Length - 1
            If Not (columnData(col) Is Nothing) AndAlso compareRow.columnData(col) Is Nothing Then
                Return 1
            ElseIf columnData(col) Is Nothing AndAlso Not (compareRow.columnData(col) Is Nothing) Then
                Return -1
            Else
                If Not (columnData(col) Is Nothing) AndAlso Not (compareRow.columnData(col) Is Nothing) Then
                    Dim result As Integer = CompareColumns(columnData(col), compareRow(col))

                    If result <> 0 Then
                        Return result
                    End If
                End If
            End If
        Next

        Return 0
    End Function

    '/ <summary>
    '/ Compares two columns.
    '/ </summary>
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")> _
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId:="System.Exception.#ctor(System.String)")> _
    Private Shared Function CompareColumns(ByVal colA As Object, ByVal colB As Object) As Integer
        Dim aType As Type = colA.GetType()
        Dim bType As Type = colB.GetType()

        If Not aType.Equals(bType) Then
            Throw New Exception("Type mismatch when comparing columns in the internal buffer.")
        End If

        If aType.Equals(GetType(String)) Then
            Return CStr(colA).CompareTo(CStr(colB))
        ElseIf aType.Equals(GetType(DateTime)) Then
            Return Convert.ToDateTime(colA, System.Globalization.CultureInfo.InvariantCulture).CompareTo(Convert.ToDateTime(colB, System.Globalization.CultureInfo.InvariantCulture))
        ElseIf aType.Equals(GetType(Decimal)) Then
            Return System.Convert.ToDecimal(colA, System.Globalization.CultureInfo.InvariantCulture).CompareTo(System.Convert.ToDecimal(colB, System.Globalization.CultureInfo.InvariantCulture))
        ElseIf aType.Equals(GetType(Guid)) Then
            Return CType(colA, Guid).CompareTo(CType(colB, Guid))
        ElseIf aType.Equals(GetType(Int16)) Then
            Return CType(colA, Int16).CompareTo(CType(colB, Int16))
        ElseIf aType.Equals(GetType(Int32)) Then
            Return CType(colA, Int32).CompareTo(CType(colB, Int32))
        ElseIf aType.Equals(GetType(Int64)) Then
            Return CType(colA, Int64).CompareTo(CType(colB, Int64))
        ElseIf aType.Equals(GetType(Boolean)) Then
            Return CBool(colA).CompareTo(CBool(colB))
        ElseIf aType.Equals(GetType(Single)) Then
            Return System.Convert.ToSingle(colA, System.Globalization.CultureInfo.InvariantCulture).CompareTo(System.Convert.ToSingle(colB, System.Globalization.CultureInfo.InvariantCulture))
        ElseIf aType.Equals(GetType(Double)) Then
            Return System.Convert.ToDouble(colA, System.Globalization.CultureInfo.InvariantCulture).CompareTo(System.Convert.ToDouble(colB, System.Globalization.CultureInfo.InvariantCulture))
        ElseIf aType.Equals(GetType(Byte)) Then
            Return Convert.ToByte(colA, System.Globalization.CultureInfo.InvariantCulture).CompareTo(Convert.ToByte(colB, System.Globalization.CultureInfo.InvariantCulture))
        ElseIf aType.Equals(GetType(UInt16)) Then
            Return CType(colA, UInt16).CompareTo(CType(colB, UInt16))
        ElseIf aType.Equals(GetType(UInt32)) Then
            Return CType(colA, UInt32).CompareTo(CType(colB, UInt32))
        ElseIf aType.Equals(GetType(UInt64)) Then
            Return CType(colA, UInt64).CompareTo(CType(colB, UInt64))
        Else
            Throw New Exception("Unsupported column data type.")
        End If
    End Function
#End Region

End Class

Public Class ColumnInfos
    Private columnInfos As ArrayList

    Public Sub New()
        columnInfos = New ArrayList()
    End Sub

    Default Public Property Item(ByVal index As Integer) As ColumnInfo
        Get
            Return CType(columnInfos(index), ColumnInfo)
        End Get

        Set(ByVal value As ColumnInfo)
            columnInfos(index) = value
        End Set
    End Property

    Public Function Add(ByVal columnInfo As ColumnInfo) As Integer
        Return columnInfos.Add(columnInfo)
    End Function

    Public ReadOnly Property Count() As Integer
        Get
            Return columnInfos.Count
        End Get
    End Property

#Region "AddColumnInformation"

    '/ <summary>
    '/ Stores the column information of the provided input and output objects in a ColumnInfo object. 
    '/ This method is called by the RemoveDuplicates component during PreExecute when the BufferManager 
    '/ is available, and assumes the exact same number of input and output columns.
    '/ </summary>
    '/ <param name="bufferManager">The IDTSBufferManager100; used to locate the columns 
    '/ in the input and output buffers.</param>
    '/ <param name="output">The output of the component.</param>
    '/ <param name="input">The input of the component.</param>
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")> _
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId:="System.Exception.#ctor(System.String)")> _
    <CLSCompliant(False)> _
    Public Sub AddColumnInformation(ByVal bufferManager As IDTSBufferManager100, ByVal output As IDTSOutput100, ByVal input As IDTSInput100)
        If input Is Nothing Then
            Throw New ArgumentNullException("input")
        End If

        If output Is Nothing Then
            Throw New ArgumentNullException("output")
        End If

        If bufferManager Is Nothing Then
            Throw New ArgumentNullException("bufferManager")
        End If

        If input.InputColumnCollection.Count <> output.OutputColumnCollection.Count Then
            Throw New Exception("Input column collection does not match the output column collection.")
        End If

        For x As Integer = 0 To output.OutputColumnCollection.Count - 1
            Dim colInfo As New ColumnInfo()
            Dim outCol As IDTSOutputColumn100 = output.OutputColumnCollection(x)
            Dim inCol As IDTSInputColumn100 = input.InputColumnCollection(x)

            ' Set the buffer column index for the input column and the output column.
            colInfo.inputBufferColumnIndex = bufferManager.FindColumnByLineageID(input.Buffer, inCol.LineageID)
            colInfo.outputBufferColumnIndex = bufferManager.FindColumnByLineageID(output.Buffer, outCol.LineageID)

            ' Save the column
            columnInfos.Add(colInfo)
        Next x
    End Sub
#End Region
End Class

<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")> _
Public Structure ColumnInfo
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")> _
    Public outputBufferColumnIndex As Integer
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")> _
    Public inputBufferColumnIndex As Integer
End Structure
